#pragma once

#include <sockets/tls.hpp>
//#include <ConnectionBroker/server/WebSocketServerHandler.h>
#include <ConnectionBroker/server/Server.h>
#include <ConnectionBroker/clientconnection/WebSocketClientConnection.h>
#include <boost/shared_ptr.hpp>
#include <boost/asio/ssl.hpp>

#include <map>

namespace ConnectionBroker
{
   //class WebSocketServerHandler;
   template <typename endpoint_type> class WebSocketServer;
   template <typename endpoint_type>
   class WebSocketServerHandler : public endpoint_type::handler
   {

   public: //Constructors, destructor
      WebSocketServerHandler(WebSocketServer<endpoint_type>* pServer, boost::shared_ptr<boost::asio::ssl::context> pContext);
      virtual ~WebSocketServerHandler();

   protected: //Member variables
      WebSocketServer<endpoint_type>* mServer;
      boost::shared_ptr<boost::asio::ssl::context> mContext;

   public:
      boost::shared_ptr<boost::asio::ssl::context> on_tls_init() { std::cout << "TLS INIT" << std::endl; return mContext; }
      void on_open(typename endpoint_type::handler::connection_ptr con);
      void on_close(typename endpoint_type::handler::connection_ptr con);
      void on_message(typename endpoint_type::handler::connection_ptr con, typename endpoint_type::handler::message_ptr msg);

   };
   template <typename endpoint_type>
   class WebSocketServer : public ConnectionBroker::Server
   {
      protected: //Member variables
      typedef WebSocketClientConnection<endpoint_type> WSCC;
      typedef boost::shared_ptr<WSCC> WSCCP;
      typedef typename endpoint_type::handler::connection_ptr CPTR;
      std::map<CPTR, WSCCP> mConnectionClientMap;
      boost::shared_ptr<boost::asio::ssl::context> mContext;

      public: //Constructor, destructor
      WebSocketServer(const ConnectionBroker::ServerSettings& settings, 
                      boost::shared_ptr<boost::asio::ssl::context> pContext,
                      serverCallback pAppMessageCallback, 
                      serverCallback pAppClientConnectCallback,
                      serverCallback pAppClientDisconnectCallback)
      : Server(settings, pAppMessageCallback, pAppClientConnectCallback, pAppClientDisconnectCallback), mContext(pContext) {}
      virtual ~WebSocketServer() {}

      public: //Interface
      WSCCP webSocketClientFactory(CPTR pCon)
      {
         //Create the new client.
         WSCCP pClient(new WebSocketClientConnection<endpoint_type>(pCon));

         //Insert the client into the client list.
         addClient(pClient);

         //Insert the record into the connection client map.
         mConnectionClientMap[pCon] = pClient;

         return pClient;
      }
      void webSocketClientRemove(CPTR pCon)
      {
         //Get the client from the connection pointer.
         WSCCP pClient = mConnectionClientMap[pCon];
         removeClient(pClient);
         mConnectionClientMap.erase(pCon);
      }

      void onMessage(CPTR pCon, const std::string& pMessage)
      {
         //Get the client from the connection pointer.
         WSCCP pClient = mConnectionClientMap[pCon];
         messageReceived(pClient, pMessage);
      }
      void start()
      {
         boost::shared_ptr<typename endpoint_type::handler> handler(new WebSocketServerHandler<endpoint_type>(this, mContext));
         endpoint_type endpoint(handler);
         endpoint.listen(mSettings.port());
      }
   };

   template <typename endpoint_type>
   WebSocketServerHandler<endpoint_type>::
   WebSocketServerHandler(WebSocketServer<endpoint_type>* pServer,
                          boost::shared_ptr<boost::asio::ssl::context> pContext)
   : endpoint_type::handler(), 
     mServer(pServer), 
     mContext(pContext) 
   {
   }

   template <typename endpoint_type>
   WebSocketServerHandler<endpoint_type>::~WebSocketServerHandler() {}

   template <typename endpoint_type> void
   WebSocketServerHandler<endpoint_type>::on_open(typename endpoint_type::handler::connection_ptr con)
   {
      mServer->webSocketClientFactory(con);
   }
   template <typename endpoint_type> void
   WebSocketServerHandler<endpoint_type>::on_close(typename endpoint_type::handler::connection_ptr con)
   {
      mServer->webSocketClientRemove(con);
   }
   template <typename endpoint_type> void
   WebSocketServerHandler<endpoint_type>::on_message(typename endpoint_type::handler::connection_ptr con, typename endpoint_type::handler::message_ptr msg)
   {
      mServer->onMessage(con, msg->get_payload());
   }


}

